// Copyright (c) 2020 Presto Labs Pte. Ltd.
// Author: xguo, taekwon

#pragma once

#include <iostream>
#include <memory>
#include <string>

#include "coin/proto/coin_telemetry.pb.h"
#include "coin2/base/request/i_session_pool.h"
#include "coin2/base/request/session_pool.h"
#include "coin2/base/window/time_window.h"
#include "coin2/exchange/base/api_base/asio_http_context.h"
#include "coin2/exchange/base/api_base/http_driver_manager.h"
#include "coin2/exchange/base/executor/live_executor.h"

namespace coin2::exchange::base::strategy_util {
class LatencyRecorder;
}

namespace coin2::exchange::base::market {
struct MarketExchangeApi;
}

namespace coin2::exchange::order {
class OrderConnectionConfig;
}

namespace coin2::exchange::base::api_base {

class NativeHttpAsioDriver {
 public:
  using SessionResponse = coin2::base::request::SessionResponse;
  using ISessionPool = coin2::base::request::ISessionPool;
  using TpsProto = coin::proto::TpsProto;
  using LatencyRecorder = coin2::exchange::base::strategy_util::LatencyRecorder;
  using MarketExchangeApi = coin2::exchange::base::market::MarketExchangeApi;
  using OrderConnectionConfig = coin2::exchange::order::OrderConnectionConfig;
  using RttInfo = coin2::base::request::RttInfo;
  using RequestStatus = coin2::base::request::RequestStatus;

 public:
  NativeHttpAsioDriver(
      boost::asio::io_context* ioc,
      const MarketExchangeApi& mea,
      const OrderConnectionConfig& cfg);

  void Start();
  void Stop();
  void PostAsyncContext(const std::shared_ptr<AsioHttpContext>& context);
  void ProcessResponse(
      const std::shared_ptr<AsioHttpContext>& context,
      int64_t request_id,
      const boost::system::error_code& ec,
      const SessionResponse* response);

  bool is_blocking() const;
  bool IsReqWindowFull(int64_t ts) const;
  void set_latency_recorder(LatencyRecorder* latency_recorder);
  const TpsProto& GetTps() const;
  void ClearReqWindow();
  RttInfo GetRttInfo() { return pool_->GetRttInfo(); }
  RequestStatus GetRequestInfo() { return pool_->GetRequestInfo(); }

 private:
  bool SendByHttp2(const std::shared_ptr<AsioHttpContext>& context) const;
  auto GetPriorityWeight(int query_type, AsioHttpContextWeight orig_weight) const
      -> AsioHttpContextWeight;

 private:
  std::shared_ptr<ISessionPool> pool_;
  // std::function<void(const RawMessage& raw_msg)> handler_;

  // TPS
  int64_t window_size_{60'000'000'000LL};
  coin2::base::window::TimeMovingWindow request_window_{window_size_};
  int64_t last_emit_time_{0};
  LatencyRecorder* latency_recorder_{nullptr};
  mutable TpsProto tps_proto_;

  // for binance only
  mutable bool is_pool_blocking_ = false;
  std::shared_ptr<ISessionPool> http2_pool_ = {};
};

}  // namespace coin2::exchange::base::api_base
